package engine;


import org.apache.log4j.Logger;
import java.io.*;
import java.util.*;

/**
 *  class Noopy
 *
 *  This is where Noopy does it's business, primarily interfacing with Winboard from here.
 *  
 *
 *  @author Brandon Ridenour
 *  Date: 2007-02-18
 */

public class Noopy implements Constants
{
	private static BufferedReader reader;
	private static String command;
	
	final static String className = Noopy.class.getName();
	final static Logger LOG = Logger.getLogger(className);
	
	/**
	 *  The main method, initializes the reader
	 *  and calls winBoard.
	 *
	 *  @param String arguments
	 */
	public static void main(String args[])
	{
		
		try{
			if(args[0].charAt(0) == 'x') // x means winboard or xboard are the calling program
			{
				
				reader = new BufferedReader(new InputStreamReader(System.in)); 
				try{
					LOG.debug("Calling WinBoard");
					winBoard(); 
				}
				catch(Exception e){}
				
			}
	
	
			else // If not winboard, stop for now.  Later we will implement UCI
				System.out.println("Bad parameter passed in");
		}catch(Exception e){
			LOG.error("Failed with exceptoin: " + e.getLocalizedMessage());
			e.printStackTrace();
		}
	}
	
	
	/**
	 *  Handles input from the winBoard application
	 *  and also sends moves and settings from the engine.
	 *
	 */
	private static void winBoard() throws IOException
	{
		Chessboard board = new Chessboard(); // Create a board on which we will be playing
		
		board.setupBoard(); 

		
		String openingLine = ""; // keep track of opening line
		boolean force = false; // Start with not being in force mode
		int increment = 0; // Initialize the increment
		int timeLeft = 0; // Initialize the timeLeft
		int searchDepth = 0; // Initialize search depth
		int movetime = 0; // Initialize fixed time per move

		// this loop will continue reading input from winboard 
		for(;;)
		{ 
			
			// receive command from winboard
			command = reader.readLine(); 
			LOG.info(command);
	
			if("xboard".equals( command )) 
			{
				// Send initialization to winboard
				System.out.println("feature myname=\"Noopy v0.3\" usermove=1 setboard=1 colors=0 analyze=0 done=1");
			}

			else if("new".equals( command )) 
			{
				// Reset everything for a new game				
				openingLine = ""; // Initilize the opening line
				board.setupBoard(); // Insert start position
				increment = 0; // Initialize the increment
				timeLeft = 0; // Initialize the timeLeft
				searchDepth = 0; // Initialize search depth
				movetime = 0;
			}

			else if("quit".equals( command )) 
			{
				// Exit the program
				System.exit(0); 
			} 

			else if(command.startsWith("setboard")) 
			{
				// received a pre-set position
				board.inputFEN(command.substring(9)); 
				increment = 0; 
				timeLeft = 0; 
				searchDepth = 0; 
				movetime = 0;
			}


			
			else if(command.startsWith("time"))
			{
				// Receive the time left
				try
				{
					// Winboard reports time left in centiseconds, transform to milliseconds
					timeLeft = Integer.parseInt(command.substring(5)) * 10;
				}
				catch(NumberFormatException ex) {timeLeft = 0;}
			}

			
			else if(command.startsWith("level"))
			{
				// Receive the time controls, we are only interested if there is an increment
				// since we will receive the time left before every move
				String[] splitString = command.split(" ");
				try
				{
					// Winboard reports increment in full seconds, transform to milliseconds
					increment = Integer.parseInt(splitString[3]) *1000;
				}
				catch(ArrayIndexOutOfBoundsException ex)
				{
					increment = 0; // If something is wrong with the level string, make increment = 0
				}
				catch(NumberFormatException ex) {}
			}


			// In force mode the engine should only register and make the moves
			// but not think or make moves of its own
			else if("force".equals( command ))
			{
				force = true;

			}

			else if(command.startsWith("st"))
			{
				try
				{
					movetime = Integer.parseInt(command.substring(3)) * 1000;
				}
				catch(NumberFormatException ex) {movetime = 0;}
			}
			
			// Tells the engine to search to a certain depth and then move
			else if(command.startsWith("sd"))
			{
				try
				{
					searchDepth = Integer.parseInt(command.substring(3));
				}
				catch(NumberFormatException ex) {}
			}

			// Opponent played a move or told us to play from the position		
			else if("go".equals( command ) || command.startsWith("usermove"))
			{

				if("go".equals( command ))
					force = false; // Exit force mode
				if(command.startsWith("usermove"))
				{
					// Receive the move and play it on the board
					Move usermove = receiveMove(command.substring(9), board);
					if (usermove == null){
						LOG.debug("receiveMove couldn't find matching move");
					}
					openingLine += command.substring(9);
					LOG.debug("Opening Line: " + openingLine);
					
					board.makeMove(usermove);
					LOG.debug("Finished -usermove-");
					
				}

				if(!force)
				{
					// Find the best line from the brain
					Brain.LineEval bestLine = new Brain.LineEval();

					
					LOG.debug("Entering Engine.search()");
					
					LOG.info("FEN before engine move:" + board.getFEN());
					
					bestLine = Brain.search(board); 
					LOG.info("Exiting Engine.search()");

					if(bestLine.line.size() != 0){ // We have found a move to make
						//  Make best move on the board
						board.makeMove((Move)bestLine.line.get(0)); 
											
						LOG.info("move " + (((Move)bestLine.line.get(0)).inputNotation()));
						LOG.info("FEN after engine move:" + board.getFEN());
						
						// send the move to winboard
						System.out.println("move " + (((Move)bestLine.line.get(0)).inputNotation()));
					}else
						LOG.info("Engine could not find valid move:  Draw or Mate");

					 
					
					//}
				}
			}
		}
	}
	// END winBoard()
	
	/**
	 *  Takes an inputted move-string and matches it with a legal move generated
	 *  from the board
	 *
	 *  @param String The inputted move
	 *  @param Chessboard The board on which to find moves
	 *  @return Move The matched move
	 */
	public static Move receiveMove(String move, Chessboard board) throws IOException
	{
		LOG.debug("receiveMove: entering");
		Vector legalMoves = board.generateMoves(false);
		LOG.debug("receiveMove: incoming move is: " + move);
		LOG.debug("legalmoves size= " + legalMoves.size());
		for(int i = 0; i < legalMoves.size(); i++)
		{
			LOG.debug("Generated Move = " + ((Move)legalMoves.get(i)).inputNotation());
			
			if(((Move)legalMoves.get(i)).inputNotation().equals(move))
				return (Move)legalMoves.get(i);
		}
		
		// If no move was found return null
		return null;
	}
	// END receiveMove()
	

	

}

